package org.jsola.hr.service.impl;

import org.jsola.approval.dto.NewApproDTO;
import org.jsola.approval.vo.UserApproVO;
import org.jsola.common.StrKit;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.DateUtils;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.common.ExcelKit;
import org.jsola.hr.constant.BalanceTypeEnum;
import org.jsola.hr.constant.EmpSocStatus;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.ICostBillDAO;
import org.jsola.hr.dto.CostBillAddDTO;
import org.jsola.hr.dto.CostBillApproveDTO;
import org.jsola.hr.dto.CostBillConfirmDTO;
import org.jsola.hr.dto.CostBillUpdateDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.provider.IApproProviderService;
import org.jsola.hr.provider.IMonthSalaryProviderService;
import org.jsola.hr.provider.ISalaryProviderService;
import org.jsola.hr.query.CostBillQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.*;
import org.jsola.json.JsonKit;
import org.jsola.orm.filter.Order;
import org.jsola.salary.entity.EmployeeSalaryDO;
import org.jsola.salary.vo.MonthlySalarySummaryVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.CostBillStatus.*;
import static org.jsola.hr.constant.HrConstants.*;

/**
 * 费用账单
 *
 * @author zhr
 */
@Service("hrCostBillServiceImpl")
public class CostBillServiceImpl implements ICostBillService {

    @Autowired
    private ICostBillDAO costBillDAO;

    @Autowired
    private IEmpSocMonthService empSocMonthService;

    @Autowired
    private IEmpSocInfoService empSocInfoService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private IEmpSocItemService empSocItemService;

    @Autowired
    private IMonthSalaryProviderService monthSalaryProviderService;

    @Autowired
    private ISalaryProviderService salaryProviderService;

    @Autowired
    private IApproProviderService approProviderService;

    @Autowired
    private ICompanyService companyService;

    @Autowired
    private IGroupCompanyService groupCompanyService;

    @Autowired
    private IChildCompanyService childCompanyService;

    @Autowired
    private ICompanyBalanceRecordService companyBalanceRecordService;

    private static final Integer TEN = 10;

    private static final Integer SOC = 1;

    private static final Integer PF = 2;

    /**
     * 保留两位小数
     */
    public final static Integer SCALE_2 = 2;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public CostBillVO save(CostBillAddDTO costBillAddDTO, TokenUser tokenUser) {
        // 转DO
        CostBillDO costBillDO = costBillAddDTO.to(CostBillDO.class);
        // 保存
        costBillDO = save(costBillDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return costBillDO.to(CostBillVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(CostBillUpdateDTO costBillUpdateDTO, TokenUser tokenUser) {
        //转DO
        CostBillDO costBillDO = costBillUpdateDTO.to(CostBillDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(costBillDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, Long... costBillIds) {
        List<CostBillDO> costBillDOList = selectByIds(Arrays.asList(costBillIds), tokenUser.getSiteId());
        for (CostBillDO costBillDO : costBillDOList) {
            if (!Boolean.TRUE.equals(costBillDO.getIsConfirm())) {
                continue;
            }
            // 写入账户余额变动
            CompanyBalanceRecordDO companyBalanceRecordDO = new CompanyBalanceRecordDO();
            companyBalanceRecordDO.setYearly(costBillDO.getYear());
            companyBalanceRecordDO.setMonthly(costBillDO.getMonth());
            companyBalanceRecordDO.setCompanyId(costBillDO.getCompanyId());
            companyBalanceRecordDO.setChangeType(BalanceTypeEnum.COST_BILL.getValue());
            companyBalanceRecordDO.setSubjectId(costBillDO.getId() + "");
            // 删除账单
            companyBalanceRecordDO.setDeleteStatus(true);
            // 费用账单是减项, 费用账单删除是加项
            companyBalanceRecordDO.setChangeAmount(costBillDO.getFinalAmount());
            companyBalanceRecordService.saveRecord(companyBalanceRecordDO, tokenUser);
        }
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) costBillIds);
    }


    @Override
    public CostBillVO selectById(Long costBillId, String siteId) {
        CostBillDO costBillDO = selectDOById(costBillId, siteId);
        if (costBillDO == null) {
            return null;
        }
        return costBillDO.to(CostBillVO.class);
    }

    @Override
    public List<CostBillDO> selectByIds(List<Long> costBillIdList, String siteId) {
        if (CollectionUtils.isEmpty(costBillIdList)) {
            return new ArrayList<>();
        }
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andIn("id", costBillIdList);
        return costBillDAO.selectByExample(example);
    }

    @Override
    public CostBillDO selectById(Long costBillId) {
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("id", costBillId);
        return costBillDAO.selectOneByExample(example);
    }

    @Override
    public List<CostBillListVO> select(CostBillQuery costBillQuery, String siteId) {
        List<CostBillDO> costBillDOList = selectDO(costBillQuery, siteId);
        if (CollectionUtils.isEmpty(costBillDOList)) {
            return costBillDOList == null ? null : new ArrayList<>();
        }
        return costBillDOList.stream()
                .map(costBillDO -> costBillDO.to(CostBillListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(CostBillQuery costBillQuery, String siteId) {
        Example example = buildExample(costBillQuery, siteId);
        return costBillDAO.selectCountByExample(example);
    }

    @Override
    public Page<CostBillListVO> selectPage(CostBillQuery costBillQuery, TokenUser tokenUser) {
        // 以用工单位名称分组
        List<Order> orders = new ArrayList<>();
        Order order = new Order();
        order.setProperty("companyName");
        order.setDesc(Boolean.FALSE);
        orders.add(order);
        costBillQuery.setOrders(orders);
        // 如果没传年月默认查询当前月
        if (ObjectUtils.isEmpty(costBillQuery.getYear()) && ObjectUtils.isEmpty(costBillQuery.getMonth())) {
            String[] yearAndMonth = DateUtils.getYearAndMonth(new Date());
            costBillQuery.setYear(Integer.parseInt(yearAndMonth[0]));
            costBillQuery.setMonth(Integer.parseInt(yearAndMonth[1]));
        }
        Example example = buildExample(costBillQuery, tokenUser.getSiteId());
        // 如果前端传用工单位分组Id,则查询用工单位分组下的所有公司账单  需要去除该用户没有的用工单位
        if (!ObjectUtils.isEmpty(costBillQuery.getIsMerge()) && costBillQuery.getIsMerge()) {
            ParamException.notNull(costBillQuery.getCompanyId(), "无效的用工单位分组");
            List<String> companyIdList =
                    groupCompanyService.selectCompanyIdListByGroupId(costBillQuery.getCompanyId(), tokenUser.getSiteId());
            // 过滤没有权限的公司
            UserCompanyVO userCompanyVO = childCompanyService.selectByUserId(tokenUser.getUserId(), tokenUser.getSiteId());
            List<String> userCompanyIds = userCompanyVO.getCompanyIds();
            if (CollectionUtils.isEmpty(userCompanyIds)) {
                userCompanyIds = groupCompanyService
                        .selectCompanyIdsByGroupIds(userCompanyVO.getGroupIds(), tokenUser.getSiteId());
            } else {
                userCompanyIds.addAll(groupCompanyService
                        .selectCompanyIdsByGroupIds(userCompanyVO.getGroupIds(), tokenUser.getSiteId()));
            }
            if (userCompanyVO.getIsAllChildCompany() != null && userCompanyVO.getIsAllChildCompany()) {
                example.and().andIn("companyId", companyIdList);
            } else {
                List<String> companyIds = new ArrayList<>();
                // 过滤没有的用工单位
                for (String companyId : companyIdList) {
                    if (userCompanyIds.contains(companyId)) {
                        companyIds.add(companyId);
                    }
                }
                example.and().andIn("companyId", companyIds);
            }
        }
        // 租户级不是用工单位分组 且companyId为0 无奈
        if (!ObjectUtils.isEmpty(costBillQuery.getIsMerge()) && !costBillQuery.getIsMerge()
                && "0".equals(costBillQuery.getCompanyId())) {
            // 过滤没有权限的公司
            UserCompanyVO userCompanyVO = childCompanyService.selectByUserId(tokenUser.getUserId(), tokenUser.getSiteId());
            // 非全部用工单位查询有权限的用工单位
            if (!userCompanyVO.getIsAllChildCompany()) {
                // 拥有的用工单位分组
                List<String> groupIds = userCompanyVO.getGroupIds();
                // 拥有的用工单位(包含拥有分组下的用工单位)
                List<String> userCompanyIds = new ArrayList<>();
                if (!CollectionUtils.isEmpty(groupIds)) {
                    userCompanyIds = groupCompanyService.selectCompanyIdListByGroupIds(groupIds, tokenUser.getSiteId());
                }
                if (!CollectionUtils.isEmpty(userCompanyVO.getCompanyIds())) {
                    userCompanyIds.addAll(userCompanyVO.getCompanyIds());
                }

                if (CollectionUtils.isEmpty(userCompanyIds)) {
                    return new Page<>(costBillQuery.getPageSize(), costBillQuery.getPageNo());
                }
                example.and().andIn("companyId", userCompanyIds);
            }
        }
        Page<CostBillDO> page = costBillDAO.selectPageByExample(example,
                costBillQuery.getPageNo(),
                costBillQuery.getPageSize());

        return page.to(CostBillListVO.class);
    }

    @Override
    public CostBillDO selectDOById(Long costBillId, String siteId) {
        return listById(costBillId, siteId);
    }

    @Override
    public CostBillDO selectByDateAndCompanyAndType(Integer year, Integer month, String companyId, String subjectType,
                                                    String siteId) {
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("companyId", companyId)
                .andEqualTo("subjectType", subjectType)
                .andEqualTo("siteId", siteId)
                .andEqualTo("valid", true);
        List<CostBillDO> costBillDOS = costBillDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(costBillDOS)) {
            return null;
        }
        return costBillDOS.get(0);
    }

    @Override
    public List<CostBillDO> selectDO(CostBillQuery costBillQuery, String siteId) {
        Example example = buildExample(costBillQuery, siteId);
        return costBillDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = TX, rollbackFor = Exception.class)
    public List<CostBillVO> generatedBills(CostBillAddDTO costBillAddDTO, TokenUser tokenUser) {
        List<CostBillVO> resultList = new ArrayList<>();
        costBillAddDTO.setApproStatus(WAIT_SETTLE.getValue());
        costBillAddDTO.setIsMerge(false);
        List<String> companyIdList = costBillAddDTO.getCompanyIdList();
        for (String companyId : companyIdList) {
            CompanyVO companyVO = companyService.selectBaseCompanyById(companyId, tokenUser.getSiteId());
            if (!ObjectUtils.isEmpty(companyVO)) {
                costBillAddDTO.setCompanyId(companyId);
                costBillAddDTO.setCompanyName(companyVO.getName());
                Integer year = costBillAddDTO.getYear();
                Integer month = costBillAddDTO.getMonth();
                String subjectType = costBillAddDTO.getSubjectType();
                CostBillDO costBillDO = selectByDateAndCompanyAndType(year, month, companyId, subjectType, tokenUser.getSiteId());
                ParamException.isNull(costBillDO, "用工单位: " + companyVO.getName() + " 下本年月已有此类型的费用账单");
                if (SOC_BILL.equals(costBillAddDTO.getSubjectType())) {
                    resultList.add(generatedBillsSoc(costBillAddDTO, tokenUser));
                } else if (PF_BILL.equals(costBillAddDTO.getSubjectType())) {
                    resultList.add(generatedBillsPf(costBillAddDTO, tokenUser));
                } else if (SALARY_BILL.equals(costBillAddDTO.getSubjectType())) {
                    resultList.add(generatedBillsSalary(costBillAddDTO, tokenUser));
                } else {
                    throw new ParamException("账单类型错误");
                }
            }
        }
        return resultList;
    }

    /**
     * 生成社保账单
     */
    private CostBillVO generatedBillsSoc(CostBillAddDTO costBillAddDTO, TokenUser tokenUser) {
        if (costBillAddDTO.getAutoSystem()) {
            List<EmpSocInfoDO> empSocInfoList = checkMonthIdAndSelectEmpInfo(costBillAddDTO, tokenUser);
            BigDecimal personalSoc = new BigDecimal("0");
            BigDecimal companySoc = new BigDecimal("0");
            BigDecimal fine = new BigDecimal("0");
            if (CollectionUtils.isEmpty(empSocInfoList)) {
                costBillAddDTO.setSubjectId("");
            } else {
                empSocInfoList = empSocInfoList.stream().filter(empSocInfoDO -> {
                    List<Integer> itemTypeList = empSocItemService
                            .selectByEmpSocId(empSocInfoDO.getId(), tokenUser.getSiteId())
                            .stream().map(EmpSocItemDO::getItemType)
                            .distinct().collect(Collectors.toList());
                    return itemTypeList.contains(SOC_ITEM_TYPE) || itemTypeList.contains(ME_ITEM_TYPE);
                }).collect(Collectors.toList());
                for (EmpSocInfoDO empSocInfoDO : empSocInfoList) {
                    personalSoc = personalSoc.add(empSocInfoDO.getPersonalSocAmount());
                    companySoc = companySoc.add(empSocInfoDO.getCompanySocAmount());
                    fine = fine.add(empSocInfoDO.getSocialSecurityFine() == null ?
                            new BigDecimal("0") : empSocInfoDO.getSocialSecurityFine());
                }
                int number = (int) empSocInfoList.stream()
                        .map(EmpSocInfoDO::getEmpId).distinct().count();
                costBillAddDTO.setNumber(number);
                costBillAddDTO.setSubjectId(empSocInfoList.get(0).getMonthId());
            }
            costBillAddDTO.setPersonalSocAmount(personalSoc);
            costBillAddDTO.setCompanySocAmount(companySoc);
            costBillAddDTO.setFine(fine);
            costBillAddDTO.setTotalAmount(personalSoc.add(companySoc).add(fine));
        } else {
            costBillAddDTO.setSubjectId("");
            costBillAddDTO.setTotalAmount(costBillAddDTO.getCompanySocAmount().add(costBillAddDTO.getPersonalSocAmount()));

        }
        costBillAddDTO.setBillName(costBillAddDTO.getYear() + "年" + costBillAddDTO.getMonth() + "月社保账单");
        return save(costBillAddDTO, tokenUser);
    }

    /**
     * 生成公积金帐单
     */
    private CostBillVO generatedBillsPf(CostBillAddDTO costBillAddDTO, TokenUser tokenUser) {
        if (costBillAddDTO.getAutoSystem()) {
            List<EmpSocInfoDO> empSocInfoList = checkMonthIdAndSelectEmpInfo(costBillAddDTO, tokenUser);
            BigDecimal personalPf = new BigDecimal("0");
            BigDecimal companyPf = new BigDecimal("0");
            BigDecimal fine = new BigDecimal("0");
            if (CollectionUtils.isEmpty(empSocInfoList)) {
                costBillAddDTO.setSubjectId("");
            } else {
                empSocInfoList = empSocInfoList.stream().filter(empSocInfoDO -> {
                    List<Integer> itemTypeList = empSocItemService
                            .selectByEmpSocId(empSocInfoDO.getId(), tokenUser.getSiteId())
                            .stream().map(EmpSocItemDO::getItemType)
                            .distinct().collect(Collectors.toList());
                    return itemTypeList.contains(PF_ITEM_TYPE);
                }).collect(Collectors.toList());
                for (EmpSocInfoDO empSocInfoDO : empSocInfoList) {
                    personalPf = personalPf.add(empSocInfoDO.getPersonalPfAmount());
                    companyPf = companyPf.add(empSocInfoDO.getCompanyPfAmount());
                    fine = fine.add(empSocInfoDO.getAccumulationFundFine() == null ?
                            new BigDecimal("0") : empSocInfoDO.getAccumulationFundFine());
                }
                int number = (int) empSocInfoList.stream()
                        .map(EmpSocInfoDO::getEmpId).distinct().count();
                costBillAddDTO.setNumber(number);
                costBillAddDTO.setSubjectId(empSocInfoList.get(0).getMonthId());
            }
            costBillAddDTO.setPersonalPfAmount(personalPf);
            costBillAddDTO.setCompanyPfAmount(companyPf);
            costBillAddDTO.setFine(fine);
            costBillAddDTO.setTotalAmount(personalPf.add(companyPf).add(fine));
        } else {
            costBillAddDTO.setSubjectId("");
            costBillAddDTO.setTotalAmount(costBillAddDTO.getCompanyPfAmount().add(costBillAddDTO.getPersonalPfAmount()));
        }
        costBillAddDTO.setBillName(costBillAddDTO.getYear() + "年" + costBillAddDTO.getMonth() + "月公积金账单");
        return save(costBillAddDTO, tokenUser);
    }

    /**
     * 生成薪资账单
     */
    private CostBillVO generatedBillsSalary(CostBillAddDTO costBillAddDTO, TokenUser tokenUser) {
        if (costBillAddDTO.getAutoSystem()) {
            MonthlySalarySummaryVO monthlySalarySummaryVO = monthSalaryProviderService.selectMonthlySalarySummary(
                    costBillAddDTO.getCompanyId(), costBillAddDTO.getYear(), costBillAddDTO.getMonth(), tokenUser);
            ParamException.notNull(monthlySalarySummaryVO, "该公司本年月无薪资账单");
            costBillAddDTO.setSubjectId(monthlySalarySummaryVO.getMonthlySalaryId() + "");
            costBillAddDTO.setCalculateSalaryNumber(monthlySalarySummaryVO.getEmpNumber());
            costBillAddDTO.setTotalAmount(monthlySalarySummaryVO.getPaidWages());
            costBillAddDTO.setTotalTax(monthlySalarySummaryVO.getTax());
        }
        costBillAddDTO.setBillName(costBillAddDTO.getYear() + "年" + costBillAddDTO.getMonth() + "月薪资账单");
        return save(costBillAddDTO, tokenUser);
    }

    /**
     * 检测传入年月是否创建参保月,并查询此月的参保信息
     */
    private List<EmpSocInfoDO> checkMonthIdAndSelectEmpInfo(CostBillAddDTO costBillsAddDTO, TokenUser tokenUser) {
        Integer billDateYear = costBillsAddDTO.getYear();
        Integer billDateMonth = costBillsAddDTO.getMonth();
        String monthAttr;
        if (billDateMonth < TEN) {
            monthAttr = "0" + billDateMonth;
        } else {
            monthAttr = billDateMonth.toString();
        }
        String monthId = empSocMonthService.selectMonthIdByYearAndMonth(costBillsAddDTO.getCompanyId(),
                billDateYear.toString(), monthAttr, tokenUser.getSiteId());
        if (StringUtils.isEmpty(monthId)) {
            throw new ParamException("该月无参保信息");
        }
        return empSocInfoService.selectEmpInfoByMonthId(costBillsAddDTO.getCompanyId(), monthId, tokenUser);
    }

    @Override
    public CostBillInfoGroupVO selectCostBillInfoById(Long id, TokenUser tokenUser) {
        CostBillDO costBillDO = selectDOById(id, tokenUser.getSiteId());
        if (ObjectUtils.isEmpty(costBillDO)) {
            throw new ParamException("未查询到账单信息");
        }
        if (SOC_BILL.equals(costBillDO.getSubjectType())) {
            return selectSocOrPfCostBillInfo(costBillDO, SOC, tokenUser);
        } else if (PF_BILL.equals(costBillDO.getSubjectType())) {
            return selectSocOrPfCostBillInfo(costBillDO, PF, tokenUser);
        } else if (SALARY_BILL.equals(costBillDO.getSubjectType())) {
            return selectSalaryCostBillInfo(costBillDO, tokenUser);
        } else {
            throw new ParamException("账单类型错误");
        }
    }

    /**
     * 查询社保或公积金账单详情
     */
    private CostBillInfoGroupVO selectSocOrPfCostBillInfo(CostBillDO costBillDO, Integer itemType, TokenUser tokenUser) {
        CostBillInfoGroupVO costBillInfoGroupVO = new CostBillInfoGroupVO();
        List<CostBillInfoVO> resultList = new ArrayList<>();
        String subjectIds = costBillDO.getSubjectId();
        if (StringUtils.isEmpty(subjectIds)) {
            return new CostBillInfoGroupVO();
        }
        String[] subjectIdArray = subjectIds.split(",");
        List<String> subjectIdList = Arrays.asList(subjectIdArray);
        List<EmpSocInfoDO> empSocInfoList =
                empSocInfoService.selectEmpInfoByMonthIdList(subjectIdList, tokenUser);
        if (CollectionUtils.isEmpty(empSocInfoList)) {
            return new CostBillInfoGroupVO();
        }
        HashSet<String> totalHeadSet = new HashSet<>();
        // 表头
        List<String> headList = new ArrayList<>();
        if (SOC.equals(itemType)) {
            headList.add("社保状态");
            headList.add("医保状态");
        } else if (PF.equals(itemType)) {
            headList.add("公积金状态");
        }
        headList.add("用工单位");
        for (EmpSocInfoDO empSocInfoDO : empSocInfoList) {
            CompanyDO companyDO = companyService.selectDOById(empSocInfoDO.getCompanyId(), tokenUser.getSiteId());
            // itemType:  1社保  2公积金
            List<EmpSocItemDO> empSocItemList =
                    empSocItemService.selectByEmpSocIdAndItemType(empSocInfoDO.getId(), itemType, tokenUser.getSiteId());
            if (SOC.equals(itemType)) {
                empSocItemList.addAll(
                        empSocItemService.selectByEmpSocIdAndItemType(empSocInfoDO.getId(), ME_ITEM_TYPE, tokenUser.getSiteId())
                );
            }
            if (CollectionUtils.isEmpty(empSocItemList)) {
                continue;
            }
            EmpInfoDO empInfoDO = empInfoService.selectDOByIdIgnoreValid(empSocInfoDO.getEmpId());
            BigDecimal totalAmount = BigDecimal.ZERO;
            BigDecimal personalAmount = BigDecimal.ZERO;
            BigDecimal companyAmount = BigDecimal.ZERO;
            // 滞纳金
            BigDecimal lateFee = BigDecimal.ZERO;
            CostBillInfoVO costBillInfoVO = new CostBillInfoVO();
            // 姓名身份证号
            costBillInfoVO.setName(empInfoDO.getName());
            costBillInfoVO.setIdCard(empInfoDO.getIdCard());
            // 动态表头数据
            Map<String, Object> headDate = new LinkedHashMap<>();
            if (SOC.equals(itemType)) {
                headDate.put("社保状态", EmpSocStatus.find(ObjectUtils.isEmpty(empSocInfoDO.getEmpSocStatus()) ?
                        0 : empSocInfoDO.getEmpSocStatus()).getDesc());
                headDate.put("医保状态", EmpSocStatus.find(ObjectUtils.isEmpty(empSocInfoDO.getEmpMeStatus()) ?
                        0 : empSocInfoDO.getEmpMeStatus()).getDesc());
            } else if (PF.equals(itemType)) {
                headDate.put("公积金状态", EmpSocStatus.find(ObjectUtils.isEmpty(empSocInfoDO.getEmpPfStatus()) ?
                        0 : empSocInfoDO.getEmpPfStatus()).getDesc());
            }
            headDate.put("用工单位", companyDO.getName());
            for (EmpSocItemDO empSocItemDO : empSocItemList) {
                if (!headList.contains(PERSONAL + empSocItemDO.getItemName())) {
                    headList.add(PERSONAL + empSocItemDO.getItemName());
                    totalHeadSet.add(PERSONAL + empSocItemDO.getItemName());
                }
                if (!headList.contains(COMPANY + empSocItemDO.getItemName())) {
                    headList.add(COMPANY + empSocItemDO.getItemName());
                    totalHeadSet.add(COMPANY + empSocItemDO.getItemName());
                }
                headDate.put(PERSONAL + empSocItemDO.getItemName(), empSocItemDO.getAmountPersonal());
                headDate.put(COMPANY + empSocItemDO.getItemName(), empSocItemDO.getAmountCompany());
                personalAmount = personalAmount.add(empSocItemDO.getAmountPersonal());
                companyAmount = companyAmount.add(empSocItemDO.getAmountCompany());
                totalAmount = personalAmount.add(companyAmount);
            }
            if (!ObjectUtils.isEmpty(empSocInfoDO.getSocialSecurityFine())) {
                totalAmount = totalAmount.add(empSocInfoDO.getSocialSecurityFine());
            }
            if (!ObjectUtils.isEmpty(empSocInfoDO.getAccumulationFundFine())) {
                totalAmount = totalAmount.add(empSocInfoDO.getAccumulationFundFine());
            }
            if (SOC.equals(itemType)) {
                headDate.put("个人社保合计", personalAmount);
                headDate.put("单位社保合计", companyAmount);
                lateFee = empSocInfoDO.getSocialSecurityFine();
            } else if (PF.equals(itemType)) {
                headDate.put("个人公积金合计", personalAmount);
                headDate.put("单位公积金合计", companyAmount);
                lateFee = empSocInfoDO.getAccumulationFundFine();
            }
            if (lateFee == null) {
                lateFee = BigDecimal.ZERO;
            }
            headDate.put("滞纳金", lateFee);
            costBillInfoVO.setHeadDate(headDate);
            costBillInfoVO.setTotalAmount(totalAmount);
            resultList.add(costBillInfoVO);

        }
        if (SOC.equals(itemType)) {
            headList.add("个人社保合计");
            headList.add("单位社保合计");
            totalHeadSet.add("个人社保合计");
            totalHeadSet.add("单位社保合计");
        } else if (PF.equals(itemType)) {
            headList.add("个人公积金合计");
            headList.add("单位公积金合计");
            totalHeadSet.add("个人公积金合计");
            totalHeadSet.add("单位公积金合计");
        }
        headList.add("滞纳金");
        totalHeadSet.add("滞纳金");
        totalHeadSet.add("总缴费");
        costBillInfoGroupVO.setHeadList(headList);
        costBillInfoGroupVO.setInfoList(resultList);
        costBillInfoGroupVO.setTotalHeadSet(totalHeadSet);
        return costBillInfoGroupVO;
    }

    /**
     * 查询薪资账单详情
     */
    private CostBillInfoGroupVO selectSalaryCostBillInfo(CostBillDO costBillDO, TokenUser tokenUser) {
        String subjectId = costBillDO.getSubjectId();
        if (StrKit.isEmpty(subjectId)) {
            return new CostBillInfoGroupVO();
        }
        String[] subjectIdArray = subjectId.split(",");
        List<String> subjectIdList = Arrays.asList(subjectIdArray);
        // 月工资汇总表id
        List<Long> monthSalaryIdList = subjectIdList.stream()
                .map(Long::parseLong).collect(Collectors.toList());
        List<EmployeeSalaryDO> employeeSalaryList = salaryProviderService.selectByMonthSalaryIds(monthSalaryIdList);
        if (CollectionUtils.isEmpty(employeeSalaryList)) {
            return new CostBillInfoGroupVO();
        }
        // 员工id
        List<String> empIdList = employeeSalaryList.parallelStream()
                .map(EmployeeSalaryDO::getEmpId).collect(Collectors.toList());
        // 员工信息
        List<EmpInfoDO> empInfoList = empInfoService.selectDoByIds(empIdList);
        // 员工id-信息 Map
        Map<String, EmpInfoDO> idEmpInfoMap = empInfoList.parallelStream()
                .collect(Collectors.toMap(EmpInfoDO::getId, Function.identity()));
        List<String> headList = new ArrayList<>();
        headList.add("用工单位");
        headList.add("工资卡号");
        headList.add("发薪银行");
        headList.add("个税");
        BigDecimal taxTotal = BigDecimal.ZERO;
        BigDecimal paidWageTotal = BigDecimal.ZERO;
        CostBillInfoGroupVO billInfoGroupVO = new CostBillInfoGroupVO();
        List<CostBillInfoVO> infoList = new ArrayList<>();
        for (EmployeeSalaryDO employeeSalaryDO : employeeSalaryList) {
            CostBillInfoVO costBillInfoVO = new CostBillInfoVO();
            Map<String, Object> headDate = new LinkedHashMap<>();
            costBillInfoVO.setName(employeeSalaryDO.getEmpName());
            CompanyDO companyDO = companyService.selectDOById(employeeSalaryDO.getCompanyId(), tokenUser.getSiteId());
            headDate.put("用工单位", companyDO.getName());
            if (idEmpInfoMap.containsKey(employeeSalaryDO.getEmpId())) {
                headDate.put("工资卡号", idEmpInfoMap.get(employeeSalaryDO.getEmpId()).getBankCard());
                headDate.put("发薪银行", idEmpInfoMap.get(employeeSalaryDO.getEmpId()).getBankName());
                BigDecimal tax = employeeSalaryDO.getTax();
                if (tax != null) {
                    tax = tax.setScale(SCALE_2, BigDecimal.ROUND_HALF_UP);
                    taxTotal = taxTotal.add(tax);
                }
                headDate.put("个税", tax);
                costBillInfoVO.setIdCard(idEmpInfoMap.get(employeeSalaryDO.getEmpId()).getIdCard());
            }
            costBillInfoVO.setHeadDate(headDate);
            costBillInfoVO.setTotalAmount(employeeSalaryDO.getPaidWages());
            paidWageTotal = paidWageTotal.add(employeeSalaryDO.getPaidWages());
            infoList.add(costBillInfoVO);
        }
        HashSet<String> totalHeadSet = new HashSet<>();
        totalHeadSet.add("个税");
        totalHeadSet.add("总缴费");
        billInfoGroupVO.setHeadList(headList);
        billInfoGroupVO.setInfoList(infoList);
        billInfoGroupVO.setTotalHeadSet(totalHeadSet);
        return billInfoGroupVO;
    }

    @Override
    public String exportCostBillInfoById(Long id, TokenUser tokenUser) {
        CostBillDO costBillDO = selectDOById(id, tokenUser.getSiteId());
        ParamException.notNull(costBillDO, "无任何导出数据");
        CostBillInfoGroupVO costBillInfoGroupVO;
        if (SOC_BILL.equals(costBillDO.getSubjectType())) {
            costBillInfoGroupVO = selectSocOrPfCostBillInfo(costBillDO, 1, tokenUser);
        } else if (PF_BILL.equals(costBillDO.getSubjectType())) {
            costBillInfoGroupVO = selectSocOrPfCostBillInfo(costBillDO, 2, tokenUser);
        } else if (SALARY_BILL.equals(costBillDO.getSubjectType())) {
            costBillInfoGroupVO = selectCostBillInfoById(costBillDO.getId(), tokenUser);
        } else {
            throw new ParamException("账单类型错误");
        }

        return export(costBillInfoGroupVO);
    }

    /**
     * 导出账单
     */
    private String export(CostBillInfoGroupVO costBillInfoGroupVO) {
        List<String> costBillInfoHead = costBillInfoGroupVO.getHeadList();
        List<CostBillInfoVO> infoList = costBillInfoGroupVO.getInfoList();
        HashSet<String> totalHeadSet = costBillInfoGroupVO.getTotalHeadSet();
        ParamException.notEmpty(costBillInfoHead, "无任何导出数据");
        // 定义表头集合
        List<List<String>> headList = new ArrayList<>();
        fillFixed(headList);
        for (String head : costBillInfoHead) {
            List<String> oneHead = new ArrayList<>();
            oneHead.add(head);
            headList.add(oneHead);
        }
        List<String> totalAmount = new ArrayList<>();
        totalAmount.add("总缴费");
        headList.add(totalAmount);
        // 定义所有的数据集合
        List<List<String>> dataList = new ArrayList<>();
        // 循环每条数据
        for (CostBillInfoVO costBillInfo : infoList) {
            List<String> oneDate = new ArrayList<>();
            Map<String, Object> headDate = costBillInfo.getHeadDate();
            for (List<String> oneHeadList : headList) {
                String head = oneHeadList.get(0);
                if ("姓名".equals(head)) {
                    oneDate.add(costBillInfo.getName());
                    continue;
                }
                if ("身份证号".equals(head)) {
                    oneDate.add(costBillInfo.getIdCard());
                    continue;
                }
                if ("总缴费".equals(head)) {
                    oneDate.add(costBillInfo.getTotalAmount().toString());
                    continue;
                }
                if (headDate.get(head) == null) {
                    oneDate.add("");
                } else {
                    oneDate.add(headDate.get(head).toString());
                }
            }
            dataList.add(oneDate);
        }
        return ExcelKit.exportExcelDynamicAndTotal("emptyCostBill", "账单信息", dataList, headList, totalHeadSet);
    }

    private void fillFixed(List<List<String>> headList) {
        List<String> name = new ArrayList<>();
        name.add("姓名");
        headList.add(name);
        List<String> idCard = new ArrayList<>();
        idCard.add("身份证号");
        headList.add(idCard);
    }

    @Override
    @Transactional(value = TX, rollbackFor = Exception.class)
    public CostBillVO generatedByCompanyIds(CostBillAddDTO costBillAddDTO, TokenUser tokenUser) {
        costBillAddDTO.setApproStatus(WAIT_SETTLE.getValue());
        costBillAddDTO.setIsMerge(true);
        ChildCompanyDO childCompanyDO =
                childCompanyService.selectDOById(costBillAddDTO.getCompanyGroupId(), tokenUser.getSiteId());
        ParamException.notNull(childCompanyDO, "无效的用工单位分组");
        List<String> companyIdList =
                groupCompanyService.selectCompanyIdListByGroupId(costBillAddDTO.getCompanyGroupId(), tokenUser.getSiteId());
        String companyIdStr = String.join(",", companyIdList);
        costBillAddDTO.setCompanyId(companyIdStr);
        costBillAddDTO.setCompanyIdList(companyIdList);
        if (SOC_BILL.equals(costBillAddDTO.getSubjectType())) {
            return generatedBillsSocByCompanyIds(costBillAddDTO, childCompanyDO, tokenUser);
        } else if (PF_BILL.equals(costBillAddDTO.getSubjectType())) {
            return generatedBillsPfByCompanyIds(costBillAddDTO, childCompanyDO, tokenUser);
        } else if (SALARY_BILL.equals(costBillAddDTO.getSubjectType())) {
            return generatedBillsSalaryByCompanyIds(costBillAddDTO, childCompanyDO, tokenUser);
        } else {
            throw new ParamException("账单类型错误");
        }
    }

    /**
     * 多用工单位生成社保账单
     */
    private CostBillVO generatedBillsSocByCompanyIds(CostBillAddDTO costBillAddDTO, ChildCompanyDO childCompanyDO, TokenUser tokenUser) {
        List<EmpSocInfoDO> empSocInfoList = checkMonthIdByCompanyIdsAndSelectEmpInfo(costBillAddDTO, tokenUser);
        BigDecimal personalSoc = new BigDecimal("0");
        BigDecimal companySoc = new BigDecimal("0");
        BigDecimal fine = new BigDecimal("0");
        if (CollectionUtils.isEmpty(empSocInfoList)) {
            costBillAddDTO.setSubjectId("");
        } else {
            for (EmpSocInfoDO empSocInfoDO : empSocInfoList) {
                personalSoc = personalSoc.add(empSocInfoDO.getPersonalSocAmount());
                companySoc = companySoc.add(empSocInfoDO.getCompanySocAmount());
                fine = fine.add(empSocInfoDO.getSocialSecurityFine());
            }
            int number = (int) empSocInfoList.stream().
                    map(EmpSocInfoDO::getEmpId).
                    distinct().count();
            costBillAddDTO.setNumber(number);
            List<String> monthIdList = empSocInfoList.parallelStream().
                    map(EmpSocInfoDO::getMonthId).
                    distinct().collect(Collectors.toList());
            costBillAddDTO.setSubjectId(String.join(",", monthIdList));
        }
        costBillAddDTO.setBillName("[" + childCompanyDO.getName() + "]" + costBillAddDTO.getYear() + "年" + costBillAddDTO.getMonth() + "月社保账单");
        costBillAddDTO.setPersonalSocAmount(personalSoc);
        costBillAddDTO.setCompanyName(childCompanyDO.getName());
        costBillAddDTO.setCompanySocAmount(companySoc);
        costBillAddDTO.setFine(fine);
        costBillAddDTO.setTotalAmount(personalSoc.add(companySoc).add(fine));
        return save(costBillAddDTO, tokenUser);
    }

    /**
     * 多用工单位生成公积金账单
     */
    private CostBillVO generatedBillsPfByCompanyIds(CostBillAddDTO costBillAddDTO, ChildCompanyDO childCompanyDO, TokenUser tokenUser) {
        List<EmpSocInfoDO> empSocInfoList = checkMonthIdByCompanyIdsAndSelectEmpInfo(costBillAddDTO, tokenUser);
        BigDecimal personalPf = new BigDecimal("0");
        BigDecimal companyPf = new BigDecimal("0");
        BigDecimal fine = new BigDecimal("0");
        if (CollectionUtils.isEmpty(empSocInfoList)) {
            costBillAddDTO.setSubjectId("");
        } else {
            for (EmpSocInfoDO empSocInfoDO : empSocInfoList) {
                personalPf = personalPf.add(empSocInfoDO.getPersonalPfAmount());
                companyPf = companyPf.add(empSocInfoDO.getCompanyPfAmount());
                fine = fine.add(empSocInfoDO.getAccumulationFundFine());
            }
            int number = (int) empSocInfoList.stream().
                    map(EmpSocInfoDO::getEmpId).
                    distinct().count();
            costBillAddDTO.setNumber(number);
            List<String> monthIdList = empSocInfoList.parallelStream().
                    map(EmpSocInfoDO::getMonthId).
                    distinct().collect(Collectors.toList());
            costBillAddDTO.setSubjectId(String.join(",", monthIdList));
        }
        costBillAddDTO.setBillName("[" + childCompanyDO.getName() + "]" + costBillAddDTO.getYear() + "年" + costBillAddDTO.getMonth() + "月公积金账单");
        costBillAddDTO.setPersonalPfAmount(personalPf);
        costBillAddDTO.setCompanyPfAmount(companyPf);
        costBillAddDTO.setFine(fine);
        costBillAddDTO.setTotalAmount(personalPf.add(companyPf).add(fine));
        costBillAddDTO.setCompanyName(childCompanyDO.getName());
        return save(costBillAddDTO, tokenUser);
    }

    /**
     * 多用工单位生成薪资账单
     */
    private CostBillVO generatedBillsSalaryByCompanyIds(CostBillAddDTO costBillAddDTO, ChildCompanyDO childCompanyDO, TokenUser tokenUser) {

        List<MonthlySalarySummaryVO> monthlySalarySummaryVOList = monthSalaryProviderService.selectMonthlySalarySummaryByCompanyIdList(
                costBillAddDTO.getCompanyIdList(), costBillAddDTO.getYear(), costBillAddDTO.getMonth(), tokenUser);
        ParamException.notEmpty(monthlySalarySummaryVOList, "该公司组本年月无薪资账单");
        String ids = monthlySalarySummaryVOList.parallelStream().map(MonthlySalarySummaryVO::getMonthlySalaryId)
                .map(Object::toString).collect(Collectors.joining(","));
        costBillAddDTO.setSubjectId(ids);

        Integer empNumber = 0;
        BigDecimal paidWages = BigDecimal.ZERO;
        BigDecimal tax = BigDecimal.ZERO;
        for (MonthlySalarySummaryVO monthlySalarySummaryVO : monthlySalarySummaryVOList) {
            empNumber += monthlySalarySummaryVO.getEmpNumber();
            paidWages = paidWages.add(monthlySalarySummaryVO.getPaidWages());
            tax = tax.add(monthlySalarySummaryVO.getTax());
        }
        costBillAddDTO.setCalculateSalaryNumber(empNumber);
        costBillAddDTO.setTotalAmount(paidWages);
        costBillAddDTO.setTotalTax(tax);
        costBillAddDTO.setBillName(costBillAddDTO.getYear() + "年" + costBillAddDTO.getMonth() + "月薪资账单");
        costBillAddDTO.setCompanyName(childCompanyDO.getName());
        return save(costBillAddDTO, tokenUser);
    }

    /**
     * 检测传入年月是否创建参保月,并查询此月的参保信息
     */
    private List<EmpSocInfoDO> checkMonthIdByCompanyIdsAndSelectEmpInfo(CostBillAddDTO costBillsAddDTO, TokenUser tokenUser) {
        Integer billDateYear = costBillsAddDTO.getYear();
        Integer billDateMonth = costBillsAddDTO.getMonth();
        String monthAttr;
        if (billDateMonth < TEN) {
            monthAttr = "0" + billDateMonth;
        } else {
            monthAttr = billDateMonth.toString();
        }
        List<String> monthIdList = empSocMonthService.selectMonthIdByYearAndMonthCompanyIdList(costBillsAddDTO.getCompanyIdList(),
                billDateYear.toString(), monthAttr, tokenUser.getSiteId());
        ParamException.notEmpty(monthIdList, "分组内所有用工单位,该月,均无人员参保记录");
        return empSocInfoService.selectEmpInfoByMonthIdList(monthIdList, tokenUser);
    }

    @Override
    public Boolean checkMonthIdCreateCostBill(String subjectId, String siteId) {
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId)
                .andLike("subjectId", "%" + subjectId + "%");
        List<CostBillDO> costBillList = costBillDAO.selectByExample(example);
        return !CollectionUtils.isEmpty(costBillList);
    }

    @Override
    @Transactional(value = TX, rollbackFor = Exception.class)
    public UserApproVO launchApproveByCompanyId(CostBillApproveDTO costBillApproveDTO, TokenUser tokenUser) {
        String approCompanyId = costBillApproveDTO.getCompanyId();
        // 指定的审批id
        String flowId = costBillApproveDTO.getFlowId();
        ParamException.hasText(costBillApproveDTO.getCompanyId(), "审批的用工单位不能为空");
        CostBillDO costBillDO = selectDOById(costBillApproveDTO.getCostBillId(), tokenUser.getSiteId());
        ParamException.notNull(costBillDO, "账单不存在");

        if (APPRO_ING.getValue().equals(costBillDO.getApproStatus())) {
            throw new ParamException("账单已经在审批中，无法再次发起审批");
        }
        if (PASS_WAIT_SETTLE.getValue().equals(costBillDO.getApproStatus())) {
            throw new ParamException("账单已经审批通过，无法再次发起审批");
        }
        if (SETTLE_END.getValue().equals(costBillDO.getApproStatus())) {
            throw new ParamException("账单已经结算，无法发起审批");
        }
        NewApproDTO newApproDTO = new NewApproDTO();
        // 这里可以放置一些自定义的数据，前端在绘制审批明细时使用。假如这里放置是账单对象
        newApproDTO.setProps(JsonKit.toJSONString(costBillDO));
        // 审批的标题
        newApproDTO.setTitle(costBillDO.getBillName());
        // 审批关联的业务类型，约定账单的subjectType是costBill
        newApproDTO.setSubjectType(SUBJECT_TYPE_COST_BILL);
        // 审批关联的业务id，账单id，可以利用这个到时可以根据账单id查询该账单的所有审批历史
        newApproDTO.setSubjectId(costBillDO.getId().toString());
        // 设置审批的流程id，costBillAppro是超级后台设置的账单审批类型的code
        String flowType;
        if (SOC_BILL.equals(costBillDO.getSubjectType())) {
            flowType = APPROVE_SOC_BILL;
        } else if (PF_BILL.equals(costBillDO.getSubjectType())) {
            flowType = APPROVE_PF_BILL;
        } else if (SALARY_BILL.equals(costBillDO.getSubjectType())) {
            flowType = APPROVE_SALARY_BILL;
        } else {
            throw new ParamException("账单类型错误");
        }
        if (!approProviderService.checkFlowId(flowType, approCompanyId, flowId, tokenUser)) {
            throw new ParamException("审批流程错误,请联系管理员");
        }
        newApproDTO.setFlowId(flowId);
        // costBillDO的所有属性会匹配审批表单里的字段，匹配到了就会设置在审批表单里
        newApproDTO.setPojo(costBillDO);
        // 发起审批
        UserApproVO userApproVO = approProviderService.addUserAppro(newApproDTO, tokenUser);
        // 设置账单成审批中
        costBillDO.setApproStatus(APPRO_ING.getValue());
        // 保存审批Id
        costBillDO.setUserApproId(userApproVO.getId());
        costBillDO.setApproCompanyId(approCompanyId);
        // 账单审批状态更新成审批中
        costBillDAO.updateByPrimaryKey(costBillDO);
        return userApproVO;
    }

    @Override
    public CostBillDO selectByMonthSalaryId(Long subjectId, TokenUser tokenUser) {
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andCondition("FIND_IN_SET('" + subjectId + "' , subject_id)");
        List<CostBillDO> costBillDOS = costBillDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(costBillDOS)) {
            return null;
        }
        return costBillDOS.get(0);
    }

    @Override
    public List<CompanyVO> selectCompanyByCostBillId(Long costBillId, TokenUser tokenUser) {
        CostBillDO costBillDO = selectDOById(costBillId, tokenUser.getSiteId());
        List<CompanyVO> companyVOList = new ArrayList<>();
        if (Objects.nonNull(costBillDO)) {
            //  查询公司id
            String companyIds = costBillDO.getCompanyId();
            if (StrKit.isEmpty(companyIds)) {
                return companyVOList;
            }
            String[] split = companyIds.split(",");
            List<String> companyIdList = Arrays.asList(split);
            List<CompanyDO> companyDOS = companyService.selectByIds(companyIdList, tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(companyDOS)) {
                companyVOList = companyDOS.stream().map(s -> s.to(CompanyVO.class)).collect(Collectors.toList());
            }
        }
        return companyVOList;
    }

    @Override
    public CostBillApprovalInfoVO selectCostBillInfoByUserApprovalId(String userApprovalId, TokenUser tokenUser) {
        CostBillApprovalInfoVO costBillApprovalInfoVO = new CostBillApprovalInfoVO();
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("userApproId", userApprovalId);
        CostBillDO costBillDO = costBillDAO.selectOneByExample(example);
        if (costBillDO == null) {
            return null;
        }
        CostBillInfoGroupVO costBillInfoGroupVO = selectCostBillInfoById(costBillDO.getId(), tokenUser);
        String annexUrl = costBillDO.getAnnexUrl();
        if (!StringUtils.isEmpty(annexUrl)) {
            costBillApprovalInfoVO.setAnnexUrl(annexUrl);
            costBillApprovalInfoVO.setAnnexName(annexUrl.substring(annexUrl.lastIndexOf("/") + 1));
        }
        costBillApprovalInfoVO.setAutoSystem(costBillDO.getAutoSystem());
        costBillApprovalInfoVO.setInfoList(costBillInfoGroupVO.getInfoList());
        costBillApprovalInfoVO.setHeadList(costBillInfoGroupVO.getHeadList());
        return costBillApprovalInfoVO;
    }

    @Override
    public List<CostBillDO> selectByCompanyId(String companyId, TokenUser tokenUser) {
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("companyId", companyId)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return costBillDAO.selectByExample(example);
    }

    @Override
    public List<CostBillDO> selectCostBillBySubjectId(String subjectId, TokenUser tokenUser) {
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andLike("subjectId", "%" + subjectId + "%")
                .andEqualTo("siteId", tokenUser.getSiteId());
        return costBillDAO.selectByExample(example);
    }

    @Override
    public Integer confirmCostBill(CostBillConfirmDTO costBillConfirmDTO, TokenUser tokenUser) {

        CostBillVO costBillVO = selectById(costBillConfirmDTO.getCostBillId(), tokenUser.getSiteId());
        ParamException.notNull(costBillVO, "费用账单为空");
        Integer status = costBillVO.getApproStatus();
        // 如果不是待结算/已通过待结算状态,则不能确认
        if (!WAIT_SETTLE.getValue().equals(status) && !PASS_WAIT_SETTLE.getValue().equals(status)) {
            throw new ParamException("只有" + WAIT_SETTLE.getDesc() + "/" + PASS_WAIT_SETTLE.getDesc() + "状态才能确认结算");
        }
        // 合并账单不能结算
        if (Boolean.TRUE.equals(costBillVO.getIsMerge())) {
            throw new ParamException("合并账单不能结算");
        }
        CostBillDO costBillDO = new CostBillDO();
        costBillDO.setId(costBillConfirmDTO.getCostBillId());
        costBillDO.setIsConfirm(true);
        costBillDO.setFinalAmount(costBillConfirmDTO.getFinalAmount());
        costBillDO.setApproStatus(SETTLE_END.getValue());
        int count = updateByIdSelective(costBillDO, tokenUser.getSiteId(), tokenUser.getUserId());
        // 写入账户余额变动
        CompanyBalanceRecordDO companyBalanceRecordDO = new CompanyBalanceRecordDO();
        companyBalanceRecordDO.setYearly(costBillVO.getYear());
        companyBalanceRecordDO.setMonthly(costBillVO.getMonth());
        companyBalanceRecordDO.setCompanyId(costBillVO.getCompanyId());
        companyBalanceRecordDO.setChangeType(BalanceTypeEnum.COST_BILL.getValue());
        companyBalanceRecordDO.setSubjectId(costBillVO.getId() + "");
        // 费用账单是减项
        companyBalanceRecordDO.setChangeAmount(BigDecimal.ZERO.subtract(costBillConfirmDTO.getFinalAmount()));
        companyBalanceRecordService.saveRecord(companyBalanceRecordDO, tokenUser);
        return count;
    }

    /**
     * 根据查询参数，构建example
     *
     * @param costBillQuery 查询参数
     * @param siteId        所属站点id
     * @return example
     */
    private Example buildExample(CostBillQuery costBillQuery, String siteId) {
        Example example = new Example(CostBillDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (costBillQuery.getMonth() != null) {
            example.and().andEqualTo("month", costBillQuery.getMonth());
        }
        if (!costBillQuery.getIsMerge() && costBillQuery.getCompanyId() != null && !"0".equals(costBillQuery.getCompanyId())) {
            example.and().andEqualTo("companyId", costBillQuery.getCompanyId());
        }
        if (costBillQuery.getYear() != null) {
            example.and().andEqualTo("year", costBillQuery.getYear());
        }
        if (costBillQuery.getIsConfirm() != null) {
            example.and().andEqualTo("isConfirm", costBillQuery.getIsConfirm());
        }
        if (costBillQuery.getStatus() != null) {
            example.and().andEqualTo("approStatus", costBillQuery.getStatus());
        }
        // 排序
        ExampleKit.setExampleOrder(example, costBillQuery.getOrders());
        return example;
    }
}




